package com.ruoyi.common.utils.ssh;

import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import expect4j.Expect4j;
import expect4j.matches.EofMatch;
import expect4j.matches.Match;
import expect4j.matches.RegExpMatch;
import expect4j.matches.TimeoutMatch;
import org.apache.oro.text.regex.MalformedPatternException;
import org.springframework.stereotype.Component;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

/**
 * 执行shell语句工具类
 */
@Component
public class CMDUtilShell {
    private static final int COMMAND_EXECUTION_SUCCESS_OPCODE = -2;
    private static final String BACKSLASH_R = "\r";

    /**
     * 执行配置命令
     *
     * @param commands 要执行的命令，为字符数组
     * @return 执行是否成功
     */
    public ExecResult executeCommands(String cmdStr, String ip, int port, String user, String password, String newpassword, Integer sshTimeout) {
        ExecResult result = new ExecResult();
        String[] commands = MessageFormat.format(cmdStr, user, newpassword).split("\r");
        Session session = null;
        ChannelShell channel = null;
        Expect4j expect = null;
        String errorMsg = null;
        try {
            session = new JSch().getSession(user, ip, port);
            session.setPassword(password);
            session.setConfig(bulidConfig());
            // 设置不提示输入密码、不显示登入信息等
            //session.setUserInfo(new LocalUserInfo());
            session.connect();
            channel = (ChannelShell) session.openChannel("shell");
            expect = new Expect4j(channel.getInputStream(), channel.getOutputStream());
            channel.connect(sshTimeout + 1000);
            // 存放返回值
            StringBuffer buffer = new StringBuffer();
            // 执行语句
            List<Match> lstPattern = bulidPattern(buffer, sshTimeout);
            for (String strCmd : commands) {
                if (expect.expect(lstPattern) != COMMAND_EXECUTION_SUCCESS_OPCODE) {

                    if (strCmd.equals("\r")) {
                        Thread.sleep(3000);
                        expect.send(BACKSLASH_R);
                    } else {
                        expect.send(strCmd);
                        expect.send(BACKSLASH_R);
                    }


                }
            }
            expect.expect(lstPattern);

            result.setCode(1);
            result.setMessages(buffer.toString());
            return result;

        } catch (Exception ex) {
            errorMsg = String.format("[@CMDUtilShell] host=[%s] user=[%s] [error=%s]", ip, user, ex.getMessage());
        } finally {
            if (expect != null) {
                expect.close();
            }
            if (channel != null) {
                channel.disconnect();
            }
            if (session != null) {
                session.disconnect();
            }
        }
        result.setCode(1);
        result.setMessages(errorMsg);
        return result;
    }

    /**
     * 构建配置项
     */
    private Hashtable<String, String> bulidConfig() {
        Hashtable<String, String> config = new Hashtable<>();
        config.put("userauth.gssapi-with-mic", "no");
        config.put("StrictHostKeyChecking", "no");
        return config;
    }

    /**
     * 构建模式
     */
    private List<Match> bulidPattern(StringBuffer buffer, Integer sshTimeout) throws MalformedPatternException {
        List<Match> lstPattern = new ArrayList<>();
        // 终止符
        // todo:存入数据库使用参数配置
        //String[] cmd=new String[]{"system-view","local-user ydz","password","admin1234","admin1234","save","y","#","y","q","quit","quit"};

        String[] regEx = {"~]#", "~#", ":~#", ">", "]", ":",};
        for (String regexElement : regEx) {
            RegExpMatch mat = new RegExpMatch(regexElement, x -> {
                buffer.append(x.getBuffer());
            });
            lstPattern.add(mat);
        }

        lstPattern.add(new EofMatch(x -> {
        }));

        // 设置超时时间
        lstPattern.add(new TimeoutMatch(sshTimeout, x -> {
        }));

        return lstPattern;
    }
}